import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QPushButton, QLabel, QFileDialog,
                             QComboBox, QSlider, QSpinBox, QAction, QMenu,
                             QMessageBox, QToolBar, QStatusBar, QDialog,
                             QFormLayout, QDialogButtonBox, QCheckBox, QDoubleSpinBox)
from PyQt5.QtGui import QPixmap, QImage, QPainter, QColor, QTransform
from PyQt5.QtCore import Qt, QRect, QPoint


class ImageProcessor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.current_img = None
        self.original_img = None
        self.file_path = None
        self.stitch_images = []  # 用于存储图像拼接的多张图片

    def initUI(self):
        # 设置窗口标题和大小
        self.setWindowTitle('简易图像处理平台')
        self.setGeometry(100, 100, 1000, 700)

        # 创建中央部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)

        # 创建图像显示区域
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_label.setMinimumSize(600, 500)
        self.image_label.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;")

        # 创建操作面板
        control_panel = QWidget()
        control_layout = QVBoxLayout(control_panel)

        # 文件操作按钮
        file_group = QWidget()
        file_layout = QHBoxLayout(file_group)

        self.open_btn = QPushButton('打开')
        self.save_btn = QPushButton('保存')
        self.stitch_btn = QPushButton('添加到拼接')
        self.stitch_process_btn = QPushButton('执行拼接')
        self.save_btn.setEnabled(False)
        self.stitch_btn.setEnabled(False)
        self.stitch_process_btn.setEnabled(False)

        self.open_btn.clicked.connect(self.open_image)
        self.save_btn.clicked.connect(self.save_image)
        self.stitch_btn.clicked.connect(self.add_to_stitch)
        self.stitch_process_btn.clicked.connect(self.stitch_images_process)

        file_layout.addWidget(self.open_btn)
        file_layout.addWidget(self.save_btn)
        file_layout.addWidget(self.stitch_btn)
        file_layout.addWidget(self.stitch_process_btn)

        # 基本图像处理操作
        basic_group = QWidget()
        basic_layout = QVBoxLayout(basic_group)

        self.resize_btn = QPushButton('调整大小')
        self.rotate_left_btn = QPushButton('向左旋转')
        self.rotate_right_btn = QPushButton('向右旋转')
        self.rotate_free_btn = QPushButton('自由旋转')
        self.flip_horizontal_btn = QPushButton('水平翻转')
        self.flip_vertical_btn = QPushButton('垂直翻转')
        self.crop_btn = QPushButton('裁剪')

        self.resize_btn.clicked.connect(self.resize_image)
        self.rotate_left_btn.clicked.connect(lambda: self.rotate_image(-90))
        self.rotate_right_btn.clicked.connect(lambda: self.rotate_image(90))
        self.rotate_free_btn.clicked.connect(self.rotate_free)
        self.flip_horizontal_btn.clicked.connect(lambda: self.flip_image(True, False))
        self.flip_vertical_btn.clicked.connect(lambda: self.flip_image(False, True))
        self.crop_btn.clicked.connect(self.crop_image)

        basic_layout.addWidget(self.resize_btn)
        basic_layout.addWidget(self.rotate_left_btn)
        basic_layout.addWidget(self.rotate_right_btn)
        basic_layout.addWidget(self.rotate_free_btn)
        basic_layout.addWidget(self.flip_horizontal_btn)
        basic_layout.addWidget(self.flip_vertical_btn)
        basic_layout.addWidget(self.crop_btn)

        # 滤镜操作
        filter_group = QWidget()
        filter_layout = QVBoxLayout(filter_group)

        filter_label = QLabel('滤镜:')
        self.filter_combo = QComboBox()
        self.filter_combo.addItems(['无', '灰度', '黑白', '反色', '模糊', '锐化', '高斯模糊', '中值滤波'])
        self.filter_combo.currentIndexChanged.connect(self.apply_filter)

        self.shape_btn = QPushButton('检测形状')
        self.shape_btn.clicked.connect(self.detect_shapes)

        filter_layout.addWidget(filter_label)
        filter_layout.addWidget(self.filter_combo)
        filter_layout.addWidget(self.shape_btn)

        # 亮度/对比度/饱和度调整
        brightness_group = QWidget()
        brightness_layout = QVBoxLayout(brightness_group)

        brightness_label = QLabel('亮度:')
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_slider.valueChanged.connect(self.adjust_brightness)

        contrast_label = QLabel('对比度:')
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(-100, 100)
        self.contrast_slider.setValue(0)
        self.contrast_slider.valueChanged.connect(self.adjust_contrast)

        saturation_label = QLabel('饱和度:')
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(-100, 100)
        self.saturation_slider.setValue(0)
        self.saturation_slider.valueChanged.connect(self.adjust_saturation)

        brightness_layout.addWidget(brightness_label)
        brightness_layout.addWidget(self.brightness_slider)
        brightness_layout.addWidget(contrast_label)
        brightness_layout.addWidget(self.contrast_slider)
        brightness_layout.addWidget(saturation_label)
        brightness_layout.addWidget(self.saturation_slider)

        # 重置按钮
        self.reset_btn = QPushButton('重置图像')
        self.reset_btn.setEnabled(False)
        self.reset_btn.clicked.connect(self.reset_image)

        # 将所有控件添加到控制面板
        control_layout.addWidget(file_group)
        control_layout.addWidget(QLabel('基本操作:'))
        control_layout.addWidget(basic_group)
        control_layout.addWidget(QLabel('滤镜效果:'))
        control_layout.addWidget(filter_group)
        control_layout.addWidget(QLabel('调整:'))
        control_layout.addWidget(brightness_group)
        control_layout.addWidget(self.reset_btn)
        control_layout.addStretch()

        # 将图像显示区域和控制面板添加到主布局
        main_layout.addWidget(self.image_label, 3)
        main_layout.addWidget(control_panel, 1)

        # 创建状态栏
        self.statusBar = QStatusBar()
        self.setStatusBar(self.statusBar)
        self.statusBar.showMessage('就绪')

        # 创建菜单栏
        self.create_menus()

    def create_menus(self):
        # 创建菜单栏
        menubar = self.menuBar()

        # 文件菜单
        file_menu = menubar.addMenu('文件')

        open_action = QAction('打开', self)
        open_action.setShortcut('Ctrl+O')
        open_action.triggered.connect(self.open_image)

        save_action = QAction('保存', self)
        save_action.setShortcut('Ctrl+S')
        save_action.triggered.connect(self.save_image)
        save_action.setEnabled(False)

        exit_action = QAction('退出', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(self.close)

        file_menu.addAction(open_action)
        file_menu.addAction(save_action)
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menubar.addMenu('编辑')

        resize_action = QAction('调整大小', self)
        resize_action.triggered.connect(self.resize_image)

        rotate_left_action = QAction('向左旋转', self)
        rotate_left_action.triggered.connect(lambda: self.rotate_image(-90))

        rotate_right_action = QAction('向右旋转', self)
        rotate_right_action.triggered.connect(lambda: self.rotate_image(90))

        rotate_free_action = QAction('自由旋转', self)
        rotate_free_action.triggered.connect(self.rotate_free)

        flip_horizontal_action = QAction('水平翻转', self)
        flip_horizontal_action.triggered.connect(lambda: self.flip_image(True, False))

        flip_vertical_action = QAction('垂直翻转', self)
        flip_vertical_action.triggered.connect(lambda: self.flip_image(False, True))

        crop_action = QAction('裁剪', self)
        crop_action.triggered.connect(self.crop_image)

        reset_action = QAction('重置图像', self)
        reset_action.triggered.connect(self.reset_image)
        reset_action.setEnabled(False)

        edit_menu.addAction(resize_action)
        edit_menu.addAction(rotate_left_action)
        edit_menu.addAction(rotate_right_action)
        edit_menu.addAction(rotate_free_action)
        edit_menu.addAction(flip_horizontal_action)
        edit_menu.addAction(flip_vertical_action)
        edit_menu.addAction(crop_action)
        edit_menu.addSeparator()
        edit_menu.addAction(reset_action)

        # 滤镜菜单
        filter_menu = menubar.addMenu('滤镜')

        for i in range(self.filter_combo.count()):
            filter_name = self.filter_combo.itemText(i)
            filter_action = QAction(filter_name, self)
            filter_action.triggered.connect(lambda checked, name=filter_name: self.apply_filter_by_name(name))
            filter_menu.addAction(filter_action)

        # 帮助菜单
        help_menu = menubar.addMenu('帮助')

        about_action = QAction('关于', self)
        about_action.triggered.connect(self.show_about)

        help_menu.addAction(about_action)

        # 存储引用以便启用/禁用
        self.save_action = save_action
        self.reset_action = reset_action

    def apply_filter_by_name(self, name):
        # 根据名称设置当前滤镜并应用
        index = self.filter_combo.findText(name)
        if index >= 0:
            self.filter_combo.setCurrentIndex(index)
            self.apply_filter()

    def open_image(self):
        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开图像", "",
            "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif *.tif *.tiff);;所有文件 (*)",
            options=options
        )

        if file_path:
            self.file_path = file_path
            # 读取图像使用OpenCV，转为RGB格式
            self.original_img = cv2.imread(file_path)
            if self.original_img is None:
                QMessageBox.warning(self, "错误", "无法读取图像文件")
                return
            self.original_img = cv2.cvtColor(self.original_img, cv2.COLOR_BGR2RGB)
            self.current_img = np.copy(self.original_img)
            self.display_image()
            self.save_btn.setEnabled(True)
            self.save_action.setEnabled(True)
            self.reset_btn.setEnabled(True)
            self.reset_action.setEnabled(True)
            self.stitch_btn.setEnabled(True)
            self.statusBar.showMessage(f'已加载图像: {os.path.basename(file_path)}')

            # 重置滑块
            self.brightness_slider.setValue(0)
            self.contrast_slider.setValue(0)
            self.saturation_slider.setValue(0)
            self.filter_combo.setCurrentIndex(0)

    def save_image(self):
        if self.current_img is None:
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存图像", "",
            "PNG (*.png);;JPEG (*.jpg *.jpeg);;BMP (*.bmp);;所有文件 (*)",
            options=options
        )

        if file_path:
            # 转回BGR格式保存
            img_to_save = cv2.cvtColor(self.current_img, cv2.COLOR_RGB2BGR)
            if cv2.imwrite(file_path, img_to_save):
                self.statusBar.showMessage(f'图像已保存至: {file_path}')
            else:
                QMessageBox.warning(self, "保存失败", "无法保存图像，请检查文件权限。")

    def display_image(self):
        if self.current_img is not None:
            # 将numpy数组转换为QImage
            height, width, channel = self.current_img.shape
            bytes_per_line = 3 * width
            q_img = QImage(self.current_img.data, width, height, bytes_per_line, QImage.Format_RGB888)
            pixmap = QPixmap.fromImage(q_img)

            # 调整图像大小以适应显示区域
            display_size = self.image_label.size()
            scaled_pixmap = pixmap.scaled(
                display_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
            )
            self.image_label.setPixmap(scaled_pixmap)

    def resize_image(self):
        if self.current_img is None:
            return

        # 创建简单的对话框获取新尺寸
        dialog = QDialog(self)
        dialog.setWindowTitle("调整大小")
        layout = QFormLayout(dialog)

        width_spin = QSpinBox()
        width_spin.setRange(1, 10000)
        width_spin.setValue(self.current_img.shape[1])

        height_spin = QSpinBox()
        height_spin.setRange(1, 10000)
        height_spin.setValue(self.current_img.shape[0])

        keep_aspect = QCheckBox("保持宽高比")
        keep_aspect.setChecked(True)

        layout.addRow("宽度:", width_spin)
        layout.addRow("高度:", height_spin)
        layout.addRow(keep_aspect)

        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(dialog.accept)
        buttons.rejected.connect(dialog.reject)
        layout.addRow(buttons)

        if dialog.exec_() == QDialog.Accepted:
            width = width_spin.value()
            height = height_spin.value()

            if keep_aspect.isChecked():
                # 保持宽高比
                ratio = self.current_img.shape[1] / self.current_img.shape[0]
                if width / height > ratio:
                    width = int(height * ratio)
                else:
                    height = int(width / ratio)

            self.current_img = cv2.resize(self.current_img, (width, height), interpolation=cv2.INTER_AREA)
            self.display_image()
            self.statusBar.showMessage(f'图像已调整为: {width}x{height}')

    def rotate_image(self, angle):
        if self.current_img is None:
            return

        rows, cols = self.current_img.shape[:2]
        M = cv2.getRotationMatrix2D((cols / 2, rows / 2), angle, 1)
        self.current_img = cv2.warpAffine(self.current_img, M, (cols, rows))
        self.display_image()
        self.statusBar.showMessage(f'图像已旋转 {angle} 度')

    def rotate_free(self):
        if self.current_img is None:
            return

        dialog = QDialog(self)
        dialog.setWindowTitle("自由旋转")
        layout = QFormLayout(dialog)

        angle_spin = QDoubleSpinBox()
        angle_spin.setRange(-360, 360)
        angle_spin.setValue(0)
        angle_spin.setSingleStep(15)

        layout.addRow("角度:", angle_spin)

        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        buttons.accepted.connect(dialog.accept)
        buttons.rejected.connect(dialog.reject)
        layout.addRow(buttons)

        if dialog.exec_() == QDialog.Accepted:
            angle = angle_spin.value()
            self.rotate_image(angle)

    def flip_image(self, horizontal, vertical):
        if self.current_img is None:
            return

        if horizontal and vertical:
            flip_code = -1  # 同时水平和垂直翻转
        elif horizontal:
            flip_code = 1  # 水平翻转
        else:
            flip_code = 0  # 垂直翻转

        self.current_img = cv2.flip(self.current_img, flip_code)
        self.display_image()
        self.statusBar.showMessage(
            f'图像已{"水平" if horizontal else ""}{"垂直" if vertical else ""}翻转'
        )

    def crop_image(self):
        if self.current_img is None:
            return

        # 创建一个临时窗口用于选择裁剪区域
        from matplotlib import pyplot as plt
        from matplotlib.widgets import RectangleSelector

        fig, ax = plt.subplots(figsize=(10, 8))
        ax.imshow(self.current_img)
        ax.set_title('拖动鼠标选择裁剪区域，按回车确认')

        rect = None

        def onselect(eclick, erelease):
            nonlocal rect
            x1, y1 = eclick.xdata, eclick.ydata
            x2, y2 = erelease.xdata, erelease.ydata
            rect = (int(min(x1, x2)), int(min(y1, y2)), int(abs(x2 - x1)), int(abs(y2 - y1)))

        rs = RectangleSelector(ax, onselect, drawtype='box')

        plt.connect('key_press_event', lambda event: plt.close() if event.key == 'enter' else None)
        plt.show()

        if rect:
            x, y, w, h = rect
            self.current_img = self.current_img[y:y + h, x:x + w]
            self.display_image()
            self.statusBar.showMessage('图像已裁剪')

    def apply_filter(self):
        if self.current_img is None:
            return

        filter_type = self.filter_combo.currentText()

        # 重置图像为原始状态
        self.current_img = np.copy(self.original_img)

        # 应用当前的亮度、对比度和饱和度设置
        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        saturation = self.saturation_slider.value() / 100

        if brightness != 0 or contrast != 0:
            self.adjust_image(brightness, contrast)

        if saturation != 0:
            self.adjust_saturation_internal(saturation)

        # 应用滤镜
        if filter_type == "灰度":
            self.apply_grayscale()
        elif filter_type == "黑白":
            self.apply_black_white()
        elif filter_type == "反色":
            self.apply_invert()
        elif filter_type == "模糊":
            self.apply_blur()
        elif filter_type == "锐化":
            self.apply_sharpen()
        elif filter_type == "高斯模糊":
            self.current_img = cv2.GaussianBlur(self.current_img, (5, 5), 0)
        elif filter_type == "中值滤波":
            self.current_img = cv2.medianBlur(self.current_img, 5)

        self.display_image()
        self.statusBar.showMessage(f'已应用滤镜: {filter_type}')

    def apply_grayscale(self):
        if self.current_img is None:
            return

        # 使用OpenCV转换为灰度图
        gray = cv2.cvtColor(self.current_img, cv2.COLOR_RGB2GRAY)
        # 转回RGB格式（每个通道值相同）
        self.current_img = cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB)

    def apply_black_white(self):
        if self.current_img is None:
            return

        gray = cv2.cvtColor(self.current_img, cv2.COLOR_RGB2GRAY)
        _, bw = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY)
        self.current_img = cv2.cvtColor(bw, cv2.COLOR_GRAY2RGB)

    def apply_invert(self):
        if self.current_img is None:
            return

        self.current_img = 255 - self.current_img

    def apply_blur(self):
        if self.current_img is None:
            return

        self.current_img = cv2.blur(self.current_img, (5, 5))

    def apply_sharpen(self):
        if self.current_img is None:
            return

        # 锐化核
        kernel = np.array([[-1, -1, -1],
                           [-1, 9, -1],
                           [-1, -1, -1]])
        self.current_img = cv2.filter2D(self.current_img, -1, kernel)

    def adjust_brightness(self):
        if self.current_img is None:
            return

        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        saturation = self.saturation_slider.value() / 100

        # 重置图像为原始状态
        self.current_img = np.copy(self.original_img)

        # 应用当前选择的滤镜
        filter_type = self.filter_combo.currentText()
        if filter_type != "无":
            self.apply_filter()
        else:
            # 仅应用亮度、对比度和饱和度
            self.adjust_image(brightness, contrast)
            if saturation != 0:
                self.adjust_saturation_internal(saturation)

        self.display_image()
        self.statusBar.showMessage(f'亮度: {brightness}, 对比度: {contrast}, 饱和度: {saturation * 100:.0f}%')

    def adjust_contrast(self):
        if self.current_img is None:
            return

        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        saturation = self.saturation_slider.value() / 100

        # 重置图像为原始状态
        self.current_img = np.copy(self.original_img)

        # 应用当前选择的滤镜
        filter_type = self.filter_combo.currentText()
        if filter_type != "无":
            self.apply_filter()
        else:
            # 仅应用亮度、对比度和饱和度
            self.adjust_image(brightness, contrast)
            if saturation != 0:
                self.adjust_saturation_internal(saturation)

        self.display_image()
        self.statusBar.showMessage(f'亮度: {brightness}, 对比度: {contrast}, 饱和度: {saturation * 100:.0f}%')

    def adjust_saturation(self):
        if self.current_img is None:
            return

        brightness = self.brightness_slider.value()
        contrast = self.contrast_slider.value()
        saturation = self.saturation_slider.value() / 100

        # 重置图像为原始状态
        self.current_img = np.copy(self.original_img)

        # 应用当前选择的滤镜
        filter_type = self.filter_combo.currentText()
        if filter_type != "无":
            self.apply_filter()
        else:
            # 仅应用亮度、对比度和饱和度
            self.adjust_image(brightness, contrast)
            self.adjust_saturation_internal(saturation)

        self.display_image()
        self.statusBar.showMessage(f'亮度: {brightness}, 对比度: {contrast}, 饱和度: {saturation * 100:.0f}%')

    def adjust_saturation_internal(self, saturation):
        # 内部函数：调整饱和度
        hsv = cv2.cvtColor(self.current_img, cv2.COLOR_RGB2HSV)
        hsv[..., 1] = np.clip(hsv[..., 1] * (1 + saturation), 0, 255)
        self.current_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)

    def adjust_image(self, brightness, contrast):
        if self.current_img is None:
            return

        # 调整亮度和对比度
        if brightness != 0:
            if brightness > 0:
                shadow = brightness
                highlight = 255
            else:
                shadow = 0
                highlight = 255 + brightness
            alpha_b = (highlight - shadow) / 255
            gamma_b = shadow

            buf = cv2.addWeighted(self.current_img, alpha_b, self.current_img, 0, gamma_b)
        else:
            buf = self.current_img.copy()

        if contrast != 0:
            f = 131 * (contrast + 127) / (127 * (131 - contrast))
            alpha_c = f
            gamma_c = 127 * (1 - f)

            buf = cv2.addWeighted(buf, alpha_c, buf, 0, gamma_c)

        self.current_img = buf

    def reset_image(self):
        if self.original_img is not None:
            self.current_img = np.copy(self.original_img)
            self.display_image()

            # 重置滑块和滤镜
            self.brightness_slider.setValue(0)
            self.contrast_slider.setValue(0)
            self.saturation_slider.setValue(0)
            self.filter_combo.setCurrentIndex(0)

            self.statusBar.showMessage('图像已重置')

    def detect_shapes(self):
        if self.current_img is None:
            return

        # 创建副本用于检测，不影响原图
        img_copy = self.current_img.copy()

        # 转换为灰度图
        gray = cv2.cvtColor(img_copy, cv2.COLOR_RGB2GRAY)

        # 高斯模糊降噪
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        # 边缘检测
        edges = cv2.Canny(blurred, 50, 150)

        # 查找轮廓
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 在原图上绘制轮廓和标签
        for contour in contours:
            # 计算轮廓面积，过滤小轮廓
            area = cv2.contourArea(contour)
            if area < 100:
                continue

            # 近似多边形
            epsilon = 0.02 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)

            # 确定形状
            vertices = len(approx)
            if vertices == 3:
                shape_name = "三角形"
                color = (0, 255, 0)  # 绿色
            elif vertices == 4:
                # 检查是否为矩形
                x, y, w, h = cv2.boundingRect(approx)
                aspect_ratio = float(w) / h
                if 0.95 <= aspect_ratio <= 1.05:
                    shape_name = "正方形"
                    color = (0, 0, 255)  # 蓝色
                else:
                    shape_name = "矩形"
                    color = (0, 165, 255)  # 橙色
            elif vertices == 5:
                shape_name = "五边形"
                color = (255, 0, 0)  # 红色
            elif vertices == 6:
                shape_name = "六边形"
                color = (255, 0, 255)  # 紫色
            else:
                # 检查是否为圆形
                perimeter = cv2.arcLength(contour, True)
                circularity = 4 * np.pi * area / (perimeter * perimeter)
                if circularity > 0.8:
                    shape_name = "圆形"
                    color = (255, 255, 0)  # 黄色
                else:
                    shape_name = "多边形"
                    color = (128, 128, 128)  # 灰色

            # 绘制轮廓
            cv2.drawContours(img_copy, [contour], -1, color, 2)

            # 计算轮廓中心
            M = cv2.moments(contour)
            if M["m00"] != 0:
                cX = int(M["m10"] / M["m00"])
                cY = int(M["m01"] / M["m00"])
                # 添加标签
                cv2.putText(img_copy, shape_name, (cX, cY),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)

        # 显示结果
        self.current_img = img_copy
        self.display_image()
        self.statusBar.showMessage(f'已检测到 {len(contours)} 个形状')

    def add_to_stitch(self):
        if self.current_img is None:
            return

        # 将当前图像添加到拼接列表
        self.stitch_images.append(np.copy(self.current_img))
        self.statusBar.showMessage(f'已添加到拼接列表，当前 {len(self.stitch_images)} 张图像')

        # 至少需要两张图像才能执行拼接
        if len(self.stitch_images) >= 2:
            self.stitch_process_btn.setEnabled(True)

    def stitch_images_process(self):
        if len(self.stitch_images) < 2:
            QMessageBox.warning(self, "警告", "至少需要两张图像进行拼接")
            return

        try:
            # 使用OpenCV的Stitcher进行图像拼接
            stitcher = cv2.Stitcher_create()
            status, result = stitcher.stitch(self.stitch_images)

            if status == cv2.Stitcher_OK:
                # 拼接成功
                self.current_img = result
                self.display_image()
                self.statusBar.showMessage('图像拼接成功')

                # 保存原始图像，以便可以重置
                self.original_img = np.copy(result)

                # 清空拼接列表
                self.stitch_images = []
                self.stitch_process_btn.setEnabled(False)
            else:
                # 拼接失败
                error_messages = {
                    cv2.Stitcher_ERR_NEED_MORE_IMGS: "需要更多图像",
                    cv2.Stitcher_ERR_HOMOGRAPHY_EST_FAIL: "无法估计单应性矩阵",
                    cv2.Stitcher_ERR_CAMERA_PARAMS_ADJUST_FAIL: "无法调整相机参数"
                }
                error_msg = error_messages.get(status, f"未知错误 (代码: {status})")
                QMessageBox.warning(self, "拼接失败", f"图像拼接失败: {error_msg}")

        except Exception as e:
            QMessageBox.critical(self, "错误", f"拼接过程中发生错误: {str(e)}")

    def show_about(self):
        QMessageBox.about(self, "关于",
                          "简易图像处理平台\n\n"
                          "一个功能齐全的图像处理工具，支持基本编辑、滤镜效果、形状识别和图像拼接。\n\n"
                          "版本: 2.0")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    processor = ImageProcessor()
    processor.show()
    sys.exit(app.exec_())
